/**
 * Copyright (c) 2018-2023, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: Condition variable
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2023-08-25     JasonHu           Init
 */

#include <base/condition.h>
#include <base/malloc.h>
#include <base/log.h>

NX_Error NX_ConditionInit(NX_Condition *condition, NX_U32 attr)
{
    if (condition == NX_NULL)
    {
        return NX_EINVAL;
    }
    NX_WaitQueueInit(&condition->condWaitQueue);
    NX_SpinInit(&condition->spinLock);
    return NX_EOK;
}

NX_Condition *NX_ConditionCreate(NX_U32 attr)
{
    NX_Condition *condition;

    condition = NX_MemAlloc(sizeof(NX_Condition));
    if (condition != NX_NULL)
    {
        NX_ConditionInit(condition, attr);
    }
    return condition;
}

NX_Error NX_ConditionDestroy(NX_Condition *condition)
{
    if (condition == NX_NULL)
    {
        return NX_EINVAL;
    }
    NX_MemFree(condition);
    return NX_EOK;
}

NX_Error NX_ConditionWait(NX_Condition *condition, NX_Mutex *mutex)
{
    NX_Error err;
    if (condition == NX_NULL || mutex == NX_NULL)
    {
        return NX_EINVAL;
    }

    /**
     * mutex must locked
     */
    if (NX_MutexIsLocked(mutex) == NX_False)
    {
        return NX_EFAULT;
    }

    err = NX_SpinLock(&condition->spinLock);
    if (err != NX_EOK)
    {
        return err;
    }

    /* unlock the mutex */
    NX_MutexUnlock(mutex);

    /* unlock spinlock and wait on condition (this is aotmic) */
    NX_WaitQueueBlockUnlock(&condition->condWaitQueue, &condition->spinLock);
    
    NX_MutexLock(mutex);
    /* lock mutex again */
    return err;
}

NX_Error NX_ConditionWaitTimeout(NX_Condition *condition, NX_Mutex *mutex, NX_TimeVal tv)
{
    NX_Error err;
    if (condition == NX_NULL || mutex == NX_NULL)
    {
        return NX_EINVAL;
    }
    
    /**
     * mutex must locked
     */
    if (NX_MutexIsLocked(mutex) == NX_False)
    {
        return NX_EFAULT;
    }

    err = NX_SpinLock(&condition->spinLock);
    if (err != NX_EOK)
    {
        return err;
    }

    /* unlock the mutex */
    NX_MutexUnlock(mutex);

    /* unlock spinlock and wait on condition (this is aotmic) */
    err = NX_WaitQueueBlockUnlockTimeout(&condition->condWaitQueue, &condition->spinLock, tv);
    
    /* lock mutex again */
    NX_MutexLock(mutex);

    return err;
}

NX_Error NX_ConditionSignal(NX_Condition *condition)
{
    NX_Error err;
    if (condition == NX_NULL)
    {
        return NX_EINVAL;
    }
    
    err = NX_SpinLock(&condition->spinLock);
    if (err != NX_EOK)
    {
        return err;
    }

    err = NX_WaitQueueWakeUp(&condition->condWaitQueue);

    NX_SpinUnlock(&condition->spinLock);
    return err;
}

NX_Error NX_ConditionBroadcast(NX_Condition *condition)
{
    NX_Error err;
    if (condition == NX_NULL)
    {
        return NX_EINVAL;
    }
    
    err = NX_SpinLock(&condition->spinLock);
    if (err != NX_EOK)
    {
        return err;
    }

    err = NX_WaitQueueWakeUpAll(&condition->condWaitQueue);

    NX_SpinUnlock(&condition->spinLock);
    return err;
}
